﻿using System;
using System.Collections.Generic;
using Microsoft.Practices.ServiceLocation;
using Microsoft.Practices.Unity;

namespace MetaContainer.Unity
{
   /// <summary>
   /// Adapter for MetaContainer using Unity as implementation of <see cref="IMetaContainer"/>.
   /// </summary>
   public sealed class UnityMetaContainerAdapter : ServiceLocatorImplBase, IMetaContainer
   {
      private readonly IUnityContainer _container;

      /// <summary>
      /// Creates new adapter instance for provided Unity container.
      /// </summary>
      /// <remarks>
      /// If not already present, registeres <see cref="FactoryMethodExtension"/> extension in the container.
      /// This extension is specific to Unity MetaContainer implementation and is used to provided factory
      /// method registration capabilities.
      /// </remarks>
      /// <param name="container">Unity container to be wrapped by <see cref="IMetaContainer"/>.</param>
      public UnityMetaContainerAdapter(IUnityContainer container)
      {
         _container = container;
         if (_container.Configure<FactoryMethodExtension>() == null)
         {
            _container.AddExtension(new FactoryMethodExtension());
         }
      }

      #region IMetaContainer
      public void Register(Type from, Type to, string name, Scope scope)
      {
         LifetimeManager lifetimeManager = MapLifetime(scope);
         _container.RegisterType(from, to, name, lifetimeManager );
      }      

      public void Register(Type from, Func<string, object> factoryMethod, Scope scope)
      {
         LifetimeManager lifetimeManager = MapLifetime(scope);
         _container.Configure<FactoryMethodExtension>().RegisterFactoryMethod(from, factoryMethod, lifetimeManager);
      }

      public IMetaContainer CreateChildContainer()
      {
         return new UnityMetaContainerAdapter(_container.CreateChildContainer());
      }
      #endregion

      #region ServiceLocatorImplBase
      protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
      {
         return _container.ResolveAll(serviceType);
      }

      protected override object DoGetInstance(Type serviceType, string key)
      {
         return _container.Resolve(serviceType, key);
      }
      #endregion

      private static LifetimeManager MapLifetime(Scope scope)
      {
         return scope == Scope.Transient
                   ? (LifetimeManager)new TransientLifetimeManager()
                   : new ContainerControlledLifetimeManager();
      }
   }
}
